#pragma once

#define CONFIG_64BIT

#define __AC(X, Y) (X##Y)
#define _AC(X, Y) __AC(X, Y)
#define _AT(T, X) ((T)(X))

#define __ASM_STR(x) #x

/* Status register flags */
#define SR_SIE _AC(0x00000002, UL)	/* Supervisor Interrupt Enable */
#define SR_MIE _AC(0x00000008, UL)	/* Machine Interrupt Enable */
#define SR_SPIE _AC(0x00000020, UL) /* Previous Supervisor IE */
#define SR_MPIE _AC(0x00000080, UL) /* Previous Machine IE */
#define SR_SPP _AC(0x00000100, UL)	/* Previously Supervisor */
#define SR_MPP _AC(0x00001800, UL)	/* Previously Machine */
#define SR_SUM _AC(0x00040000, UL)	/* Supervisor User Memory Access */

#define SR_FS _AC(0x00006000, UL) /* Floating-point Status */
#define SR_FS_OFF _AC(0x00000000, UL)
#define SR_FS_INITIAL _AC(0x00002000, UL)
#define SR_FS_CLEAN _AC(0x00004000, UL)
#define SR_FS_DIRTY _AC(0x00006000, UL)

#define SR_XS _AC(0x00018000, UL) /* Extension Status */
#define SR_XS_OFF _AC(0x00000000, UL)
#define SR_XS_INITIAL _AC(0x00008000, UL)
#define SR_XS_CLEAN _AC(0x00010000, UL)
#define SR_XS_DIRTY _AC(0x00018000, UL)

#ifndef CONFIG_64BIT
#define SR_SD _AC(0x80000000, UL) /* FS/XS dirty */
#else
#define SR_SD _AC(0x8000000000000000, UL) /* FS/XS dirty */
#endif

/* SATP flags */
#ifndef CONFIG_64BIT
#define SATP_PPN _AC(0x003FFFFF, UL)
#define SATP_MODE_32 _AC(0x80000000, UL)
#define SATP_MODE SATP_MODE_32
#else
#define SATP_PPN _AC(0x00000FFFFFFFFFFF, UL)
#define SATP_MODE_39 _AC(0x8000000000000000, UL)
#define SATP_MODE SATP_MODE_39
#endif

/* Exception cause high bit - is an interrupt if set */
#define CAUSE_IRQ_FLAG (_AC(1, UL) << (__riscv_xlen - 1))

/* Interrupt causes (minus the high bit) */
#define IRQ_S_SOFT 1
#define IRQ_M_SOFT 3
#define IRQ_S_TIMER 5
#define IRQ_M_TIMER 7
#define IRQ_S_EXT 9
#define IRQ_M_EXT 11

/* Exception causes */
#define EXC_INST_MISALIGNED 0
#define EXC_INST_ACCESS 1
#define EXC_BREAKPOINT 3
#define EXC_LOAD_ACCESS 5
#define EXC_STORE_ACCESS 7
#define EXC_SYSCALL 8
#define EXC_INST_PAGE_FAULT 12
#define EXC_LOAD_PAGE_FAULT 13
#define EXC_STORE_PAGE_FAULT 15

/* PMP configuration */
#define PMP_R 0x01
#define PMP_W 0x02
#define PMP_X 0x04
#define PMP_A 0x18
#define PMP_A_TOR 0x08
#define PMP_A_NA4 0x10
#define PMP_A_NAPOT 0x18
#define PMP_L 0x80

/* symbolic CSR names: */
#define CSR_CYCLE 0xc00
#define CSR_TIME 0xc01
#define CSR_INSTRET 0xc02
#define CSR_CYCLEH 0xc80
#define CSR_TIMEH 0xc81
#define CSR_INSTRETH 0xc82

#define CSR_SSTATUS 0x100
#define CSR_SIE 0x104
#define CSR_STVEC 0x105
#define CSR_SCOUNTEREN 0x106
#define CSR_SSCRATCH 0x140
#define CSR_SEPC 0x141
#define CSR_SCAUSE 0x142
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
#define CSR_SATP 0x180

#define CSR_MSTATUS 0x300
#define CSR_MISA 0x301
#define CSR_MIE 0x304
#define CSR_MTVEC 0x305
#define CSR_MSCRATCH 0x340
#define CSR_MEPC 0x341
#define CSR_MCAUSE 0x342
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
#define CSR_PMPCFG0 0x3a0
#define CSR_PMPADDR0 0x3b0
#define CSR_MHARTID 0xf14

#ifdef CONFIG_RISCV_M_MODE
#define CSR_STATUS CSR_MSTATUS
#define CSR_IE CSR_MIE
#define CSR_TVEC CSR_MTVEC
#define CSR_SCRATCH CSR_MSCRATCH
#define CSR_EPC CSR_MEPC
#define CSR_CAUSE CSR_MCAUSE
#define CSR_TVAL CSR_MTVAL
#define CSR_IP CSR_MIP

#define SR_IE SR_MIE
#define SR_PIE SR_MPIE
#define SR_PP SR_MPP

#define RV_IRQ_SOFT IRQ_M_SOFT
#define RV_IRQ_TIMER IRQ_M_TIMER
#define RV_IRQ_EXT IRQ_M_EXT
#else /* CONFIG_RISCV_M_MODE */
#define CSR_STATUS CSR_SSTATUS
#define CSR_IE CSR_SIE
#define CSR_TVEC CSR_STVEC
#define CSR_SCRATCH CSR_SSCRATCH
#define CSR_EPC CSR_SEPC
#define CSR_CAUSE CSR_SCAUSE
#define CSR_TVAL CSR_STVAL
#define CSR_IP CSR_SIP

#define SR_IE SR_SIE
#define SR_PIE SR_SPIE
#define SR_PP SR_SPP

#define RV_IRQ_SOFT IRQ_S_SOFT
#define RV_IRQ_TIMER IRQ_S_TIMER
#define RV_IRQ_EXT IRQ_S_EXT
#endif /* CONFIG_RISCV_M_MODE */

/* IE/IP (Supervisor/Machine Interrupt Enable/Pending) flags */
#define IE_SIE (_AC(0x1, UL) << RV_IRQ_SOFT)
#define IE_TIE (_AC(0x1, UL) << RV_IRQ_TIMER)
#define IE_EIE (_AC(0x1, UL) << RV_IRQ_EXT)

// Supervisor Status Register, sstatus
#define SSTATUS_SPP (1L << 8)  // Previous mode, 1=Supervisor, 0=User
#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable
#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable
#define SSTATUS_SIE (1L << 1)  // Supervisor Interrupt Enable
#define SSTATUS_UIE (1L << 0)  // User Interrupt Enable

// Supervisor Interrupt Enable
#define SIE_SEIE (1L << 9) // external
#define SIE_STIE (1L << 5) // timer
#define SIE_SSIE (1L << 1) // software

// Machine Status Register, mstatus
#define MSTATUS_UIE			(1 << 0)
#define MSTATUS_SIE			(1 << 1)
#define MSTATUS_MIE			(1 << 3) // machine-mode interrupt enable.
#define MSTATUS_UPIE		(1 << 4)
#define MSTATUS_SPIE		(1 << 5)
#define MSTATUS_MPIE		(1 << 7)
#define MSTATUS_SPP			(1 << 8)
#define MSTATUS_MPP			(3 << 11)
#define MSTATUS_MPP_MASK	(3 << 11) // previous mode.
#define MSTATUS_MPP_M		(3 << 11)
#define MSTATUS_MPP_S		(1 << 11)
#define MSTATUS_MPP_U		(0 << 11)
#define MSTATUS_FS			(3 << 13)
#define MSTATUS_XS			(3 << 15)
#define MSTATUS_MPRV		(1 << 17)
#define MSTATUS_SUM			(1 << 18)
#define MSTATUS_MXR			(1 << 19)
#define MSTATUS_TVM			(1 << 20)
#define MSTATUS_TW			(1 << 21)
#define MSTATUS_TSR			(1 << 22)
#define MSTATUS32_SD		(1 << 31)
#define MSTATUS_UXL			(3ULL << 32)
#define MSTATUS_SXL			(3ULL << 34)
#define MSTATUS64_SD		(1ULL << 63)

#define MIP_USIP			(1 << 0)
#define MIP_SSIP			(1 << 1)
#define MIP_MSIP			(1 << 3)
#define MIP_UTIP			(1 << 4)
#define MIP_STIP			(1 << 5)
#define MIP_MTIP			(1 << 7)
#define MIP_UEIP			(1 << 8)
#define MIP_SEIP			(1 << 9)
#define MIP_MEIP			(1 << 11)

// Machine-mode Interrupt Enable
#define MIE_USIE			(1 << 0)
#define MIE_SSIE			(1 << 1)
#define MIE_MSIE			(1 << 3)  // software
#define MIE_UTIE			(1 << 4)
#define MIE_STIE			(1 << 5)
#define MIE_MTIE			(1 << 7)  // timer
#define MIE_UEIE			(1 << 8)
#define MIE_SEIE			(1 << 9)
#define MIE_MEIE			(1 << 11) // external

#define csr_swap(csr, val)                                      \
	({                                                          \
		unsigned long __v = (unsigned long)(val);               \
		__asm__ __volatile__("csrrw %0, " __ASM_STR(csr) ", %1" \
							 : "=r"(__v)                        \
							 : "rK"(__v)                        \
							 : "memory");                       \
		__v;                                                    \
	})

#define csr_read(csr)                                   \
	({                                                  \
		register unsigned long __v;                     \
		__asm__ __volatile__("csrr %0, " __ASM_STR(csr) \
							 : "=r"(__v)                \
							 :                          \
							 : "memory");               \
		__v;                                            \
	})

#define csr_write(csr, val)                                \
	({                                                     \
		unsigned long __v = (unsigned long)(val);          \
		__asm__ __volatile__("csrw " __ASM_STR(csr) ", %0" \
							 :                             \
							 : "rK"(__v)                   \
							 : "memory");                  \
	})

#define csr_read_set(csr, val)                                  \
	({                                                          \
		unsigned long __v = (unsigned long)(val);               \
		__asm__ __volatile__("csrrs %0, " __ASM_STR(csr) ", %1" \
							 : "=r"(__v)                        \
							 : "rK"(__v)                        \
							 : "memory");                       \
		__v;                                                    \
	})

#define csr_set(csr, val)                                  \
	({                                                     \
		unsigned long __v = (unsigned long)(val);          \
		__asm__ __volatile__("csrs " __ASM_STR(csr) ", %0" \
							 :                             \
							 : "rK"(__v)                   \
							 : "memory");                  \
	})

#define csr_read_clear(csr, val)                                \
	({                                                          \
		unsigned long __v = (unsigned long)(val);               \
		__asm__ __volatile__("csrrc %0, " __ASM_STR(csr) ", %1" \
							 : "=r"(__v)                        \
							 : "rK"(__v)                        \
							 : "memory");                       \
		__v;                                                    \
	})

#define csr_clear(csr, val)                                \
	({                                                     \
		unsigned long __v = (unsigned long)(val);          \
		__asm__ __volatile__("csrc " __ASM_STR(csr) ", %0" \
							 :                             \
							 : "rK"(__v)                   \
							 : "memory");                  \
	})

#define CSR_CYCLE		0xc00
#define CSR_TIME		0xc01
#define CSR_INSTRET		0xc02
#define CSR_SSTATUS		0x100
#define CSR_SIE			0x104
#define CSR_STVEC		0x105
#define CSR_SCOUNTEREN		0x106
#define CSR_SSCRATCH		0x140
#define CSR_SEPC		0x141
#define CSR_SCAUSE		0x142
#define CSR_STVAL		0x143
#define CSR_SIP			0x144
#define CSR_SATP		0x180
#define CSR_CYCLEH		0xc80
#define CSR_TIMEH		0xc81
#define CSR_INSTRETH		0xc82